package com.jabex.manager.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.ApplicationFactory;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
 * Created by Jerges Bello on 15/07/2014.
 * <p/>
 * Utility class to support multilingual FacesMessages using ResourceBundles.
 * Standard messages are stored at <code>DEFAULT_BUNDLE</code>.<br>
 * The summary of the message is stored at the requested key value. The detail
 * of the message is stored at &lt;messageId&gt;_detail.
 *
 * @author Thomas Spiegl (latest modification by $Author$)
 * @author Manfred Geiler
 * @author Sean Schofield
 * @author Stpehan Strittmatter
 * @version $Revision$ $Date$
 * @see FacesMessage
 * @see java.util.ResourceBundle
 */
public final class MessageUtils { // NO_UCD

	/**
	 * Utility class, do not instatiate
	 */
	private MessageUtils() {
		// nope
	}

	/**
	 * Default bundle for messages (<code>javax.faces.Messages</code>)
	 */
	private static final String DEFAULT_BUNDLE = "javax.faces.Messages";

	/**
	 * Suffix for message details (<code>_detail</code>)
	 */
	private static final String DETAIL_SUFFIX = "_detail";
	private static Log log = LogFactory.getLog(MessageUtils.class);

	/**
	 * @param severity  serverity of message
	 * @param messageId id of message
	 * @param arg       arument of message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(FacesMessage.Severity severity,
	                                      String messageId,
	                                      Object arg) {
		return getMessage(null, severity,
				messageId,
				new Object[]{arg},
				FacesContext.getCurrentInstance());
	}

	/**
	 * @param severity  serverity of message
	 * @param messageId id of message
	 * @param args      aruments of message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(FacesMessage.Severity severity,
	                                      String messageId,
	                                      Object[] args) {
		return getMessage(null, severity,
				messageId,
				args,
				FacesContext.getCurrentInstance());
	}

	public static FacesMessage getMessage(String messageBundle, FacesMessage.Severity severity,
	                                      String messageId,
	                                      Object[] args,
	                                      FacesContext facesContext) {
		FacesMessage message = getMessage(messageBundle, facesContext, messageId, args);
		message.setSeverity(severity);

		return message;
	}

	public static void addMessage(String messageBundle, FacesMessage.Severity severity,
	                              String messageId, Object[] args) {
		addMessage(messageBundle, severity, messageId, args, null, FacesContext.getCurrentInstance());
	}

	public static void addMessage(FacesMessage.Severity severity,
	                              String messageId,
	                              Object[] args,
	                              FacesContext facesContext) {
		addMessage(null, severity, messageId, args, null, facesContext);
	}

	public static void addMessage(String messageBundle, FacesMessage.Severity severity,
	                              String messageId, Object[] args, String forClientId) {
		addMessage(messageBundle, severity, messageId, args, forClientId, FacesContext.getCurrentInstance());
	}

	public static void addMessage(String messageBundle, FacesMessage.Severity severity,
	                              String messageId,
	                              Object[] args,
	                              String forClientId,
	                              FacesContext facesContext) {
		if (log.isTraceEnabled()) {
			log.trace("adding message " + messageId + " for clientId " + forClientId);
		}
		facesContext.addMessage(forClientId,
				getMessage(messageBundle, severity, messageId, args, facesContext));
	}

	/**
	 * Uses <code>MessageFormat</code> and the supplied parameters to fill in the param placeholders in the String.
	 *
	 * @param locale  The <code>Locale</code> to use when performing the substitution.
	 * @param msgtext The original parameterized String.
	 * @param params  The params to fill in the String with.
	 * @return The updated String.
	 */
	public static String substituteParams(Locale locale, String msgtext, Object params[]) {

		if (params == null || msgtext == null)
			return msgtext;
		MessageFormat mf = new MessageFormat(msgtext);
		if (locale != null) {
			mf.setLocale(locale);
			return mf.format(params);
		}
		return null;
	}

	public static FacesMessage getMessage(String messageId, Object params[]) {
		Locale locale;
		FacesContext context = FacesContext.getCurrentInstance();
		if (context != null && context.getViewRoot() != null) {
			locale = context.getViewRoot().getLocale();
			if (locale == null)
				locale = Locale.getDefault();
		} else {
			locale = Locale.getDefault();
		}
		return getMessage(locale, messageId, params, null);
	}

	/**
	 * Gets the ClassLoader associated with the current thread.  Returns the class loader associated with
	 * the specified default object if no context loader is associated with the current thread.
	 *
	 * @param defaultObject The default object to use to determine the class loader (if none associated with current thread.)
	 * @return ClassLoader
	 */
	protected static ClassLoader getCurrentLoader(Object defaultObject) {
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		if (loader == null) {
			loader = defaultObject.getClass().getClassLoader();
		}
		return loader;
	}


	public static FacesMessage getMessage(Locale locale, String messageId, Object params[], String messageBundle) {
		String summary = null;
		String detail = null;
		String bundleName;
		if (messageBundle != null && messageBundle.trim().length() > 0)
			bundleName = messageBundle;
		else
			bundleName = getApplication().getMessageBundle();

		ResourceBundle bundle = null;
		if (bundleName != null) {
			try {
				bundle = ResourceBundle.getBundle(bundleName, locale, getCurrentLoader(bundleName));
				summary = bundle.getString(messageId);
			} catch (MissingResourceException e) {
				// NoOp
			}
		}

		if (summary == null) {
			try {
				bundle = ResourceBundle.getBundle(DEFAULT_BUNDLE, locale, getCurrentLoader(DEFAULT_BUNDLE));
				if (bundle == null) {
					throw new NullPointerException();
				}
				summary = bundle.getString(messageId);
			} catch (MissingResourceException e) {
				// NoOp
			}
		}

		if (summary == null) {
			summary = messageId;
		}

		if (bundle == null) {
			throw new NullPointerException(
					"Unable to locate ResrouceBundle: bundle is null");
		}
		summary = substituteParams(locale, summary, params);

		try {
			detail = substituteParams(locale,
					bundle.getString(messageId + DETAIL_SUFFIX), params);
		} catch (MissingResourceException e) {
			// NoOp
		}

		return new FacesMessage(summary, detail);
	}

	/**
	 * @param bundleBaseName baseName of ResourceBundle to load localized messages
	 * @param messageId      id of message
	 * @param params         parameters to set at localized message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(String bundleBaseName, String messageId, Object params[]) {
		return getMessage(bundleBaseName, getCurrentLocale(), messageId, params);
	}

	/**
	 * @return currently applicable Locale for this request.
	 */
	public static Locale getCurrentLocale() {
		Locale locale;

		FacesContext context = FacesContext.getCurrentInstance();
		if (context != null && context.getViewRoot() != null) {
			locale = context.getViewRoot().getLocale();
			if (locale == null)
				locale = Locale.getDefault();
		} else {
			locale = Locale.getDefault();
		}

		return locale;
	}

	/**
	 * @param severity       severity of message
	 * @param bundleBaseName baseName of ResourceBundle to load localized messages
	 * @param messageId      id of message
	 * @param params         parameters to set at localized message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(FacesMessage.Severity severity, String bundleBaseName, String messageId, Object params[]) {
		FacesMessage msg = getMessage(bundleBaseName, messageId, params);
		msg.setSeverity(severity);

		return msg;
	}

	/**
	 * @param bundleBaseName baseName of ResourceBundle to load localized messages
	 * @param locale         current locale
	 * @param messageId      id of message
	 * @param params         parameters to set at localized message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(String bundleBaseName, Locale locale, String messageId, Object params[]) {
		if (bundleBaseName == null) {
			throw new NullPointerException(
					"Unable to locate ResourceBundle: bundle is null");
		}

		ResourceBundle bundle = ResourceBundle.getBundle(bundleBaseName, locale);

		return getMessage(bundle, messageId, params);
	}

	/**
	 * @param bundle    ResourceBundle to load localized messages
	 * @param messageId id of message
	 * @param params    parameters to set at localized message
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(ResourceBundle bundle, String messageId, Object params[]) {

		String summary = null;
		String detail = null;

		try {
			summary = bundle.getString(messageId);
		} catch (MissingResourceException e) {
			// NoOp
		}


		if (summary == null) {
			summary = messageId;
		}

		summary = substituteParams(bundle.getLocale(), summary, params);

		try {
			detail = substituteParams(bundle.getLocale(),
					bundle.getString(messageId + DETAIL_SUFFIX), params);
		} catch (MissingResourceException e) {
			// NoOp
		}

		return new FacesMessage(summary, detail);
	}

	/**
	 * @param context   context
	 * @param messageId messageId
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(FacesContext context, String messageId) {
		return getMessage(null, context, messageId, null);
	}

	/**
	 * @param context   context
	 * @param messageId messageId
	 * @param params    params
	 * @return generated FacesMessage
	 */
	public static FacesMessage getMessage(String messageBundle, FacesContext context,
	                                      String messageId, Object params[]) {
		if (context == null || messageId == null)
			throw new NullPointerException(" context " + context + " messageId " + messageId);
		Locale locale = getCurrentLocale();
		FacesMessage message = getMessage(locale, messageId, params, messageBundle);
		if (message != null) {
			return message;
		} else {
			locale = Locale.getDefault();
			return getMessage(locale, messageId, params, messageBundle);
		}
	}

	private static Application getApplication() {
		FacesContext context = FacesContext.getCurrentInstance();
		if (context != null) {
			return FacesContext.getCurrentInstance().getApplication();
		} else {
			ApplicationFactory afactory = (ApplicationFactory) FactoryFinder.getFactory("javax.faces.application.ApplicationFactory");
			return afactory.getApplication();
		}
	}
}
